package com.ass.freedom.net.handler;

import com.ass.freedom.net.protocol.FMessage;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 客户端操作类
 * @author syq
 * @date 2019/12/31
 */
@Slf4j
public class ClientOps {

    private final EventLoop eventLoop;

    private Map<Integer, Promise<FMessage>> waitingPromises = new ConcurrentHashMap<>();

    private volatile Channel channel;



    public ClientOps(Channel channel, EventLoop eventLoop) {
        this.eventLoop = eventLoop;
        this.channel = channel;
    }

    public boolean send(FMessage msg){
        Channel chnl = channel;
        if(null == chnl){
            return false;
        }

        if(!chnl.isActive()){
            return false;
        }
        chnl.writeAndFlush(msg);
        return true;
    }


    public Promise<FMessage> send(FMessage msg, TimeUnit timeUnit, long timeout){
        Channel chnl = channel;

        Promise<FMessage> res = new DefaultPromise<>(eventLoop);
        if(null == chnl){
            res.tryFailure(new Exception("channel is null"));
            return res;
        }

        if(!chnl.isActive()){
            res.tryFailure(new Exception("chanell is inactive"));
            return res;
        }

        waitResponse(msg, res, timeUnit, timeout);
        chnl.writeAndFlush(msg);
        return res;
    }

    protected void waitResponse(FMessage msg, Promise<FMessage> res, TimeUnit timeUnit, long timeout){
        ScheduledFuture<?> future = eventLoop.schedule(new Runnable() {
            @Override
            public void run() {
                Promise<FMessage> wres = waitingPromises.remove(msg.getSequenceId());
                if(null == wres){
                    return;
                }
                if(wres.isDone()){
                    return;
                }
                wres.tryFailure(new Exception("response timeout"));
            }
        }, timeout, timeUnit);
        waitingPromises.put(msg.getSequenceId(), res);
    }

    public void onResponse(FMessage msg) {

        if(null == msg){
            log.warn("内容消息未接收到！");
            return;
        }
        Promise<FMessage> res = waitingPromises.remove(msg.getSequenceId());
        if(null == res){
            return;
        }

        res.trySuccess(msg);
    }


    public void disconnect() {
        channel.close();
    }

}
